package com.example.novelweb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.novelweb.common.BaseResponse;
import com.example.novelweb.common.ErrorCode;
import com.example.novelweb.common.ResultUtils;
import com.example.novelweb.constant.CommonConstant;
import com.example.novelweb.constant.UserConstant;
import com.example.novelweb.exception.BusinessException;
import com.example.novelweb.exception.ThrowUtils;
import com.example.novelweb.model.dto.novel.NovelAddRequest;
import com.example.novelweb.model.dto.novel.NovelQueryRequest;
import com.example.novelweb.model.dto.novel.NovelUpdateRequest;
import com.example.novelweb.model.entity.Novel;
import com.example.novelweb.model.entity.Tagmap;
import com.example.novelweb.model.vo.NovelInfo;
import com.example.novelweb.model.vo.NovelVO;
import com.example.novelweb.model.vo.UserVO;
import com.example.novelweb.service.*;
import com.example.novelweb.mapper.NovelMapper;
import com.example.novelweb.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author PERSON
* @description 针对表【novel(小说)】的数据库操作Service实现
* @createDate 2025-01-10 18:32:23
*/
@Service
public class NovelServiceImpl extends ServiceImpl<NovelMapper, Novel>
    implements NovelService{

  @Resource
  private CollectService collectService;
  @Resource
  private TagService tagService;

  @Resource
  private TagmapService tagmapService;

  @Resource
  private UserService userService;

  @Resource
  private PermissionService permissionService;

  @Override
  public BaseResponse<Page<NovelVO>> pageAll(NovelQueryRequest novelQueryRequest) {
    // queryWrapper
    String sortField = novelQueryRequest.getSortField();
    String sortOrder = novelQueryRequest.getSortOrder();
    QueryWrapper<NovelVO> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderBy(SqlUtils.validSortField(sortField),
        sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField);

    int current = novelQueryRequest.getCurrent();
    int pageSize = novelQueryRequest.getPageSize();
    Page<NovelVO> page = getBaseMapper().pageNovelVO(new Page<>(current,pageSize),queryWrapper);


    // set tags
    for(NovelVO n : page.getRecords()) {
      n.setTags(tagService.getTagsByNovelId(n.getId()));
    }

    return ResultUtils.success(page);
  }


  @Override
  public BaseResponse<Page<NovelVO>> pageBySearch(NovelQueryRequest novelQueryRequest) {
    String name = novelQueryRequest.getName();
    String userName = novelQueryRequest.getUserName();

    String sortField = novelQueryRequest.getSortField();
    String sortOrder = novelQueryRequest.getSortOrder();

    int current = novelQueryRequest.getCurrent();
    int pageSize = novelQueryRequest.getPageSize();

    QueryWrapper<NovelVO> queryWrapper = new QueryWrapper<>();

    queryWrapper.like(StringUtils.isNotBlank(name), "n.name", name);
    queryWrapper.or().like(StringUtils.isNotBlank(userName), "u.nickname", userName);

    queryWrapper.orderBy(SqlUtils.validSortField(sortField),
        sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField);

    String tagsList = novelQueryRequest.getTags();

    Page<NovelVO> page = null;
    if(StringUtils.isBlank(tagsList)) {
      page = getBaseMapper().pageNovelVO(new Page<>(current,pageSize),queryWrapper);
    }
    else {
      // 校验tags
      if(RegexUtils.isListStrInvalid(tagsList)) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR,"param tags invalid");
      }
      // 去重
      Set<Long> set = Arrays.stream(tagsList.split(","))
          .map(Long::parseLong)
          .collect(Collectors.toSet());
      // format string "1,2,3,..."
      String setString = set.stream()
          .map(String::valueOf)
          .collect(Collectors.joining(","));

      page = getBaseMapper().pageVOByKeys(new Page<>(current,pageSize)
          ,queryWrapper,setString, set.size());
    }

    // get novel's tags
    for(NovelVO n : page.getRecords()) {
      n.setTags(tagService.getTagsByNovelId(n.getId()));
    }

    return ResultUtils.success(page);

  }



  @Override
  public BaseResponse<NovelInfo> getInfo(Long id) {
    Novel novel = query().eq("id",id).one();
    if(novel == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR,"not exist this id novel");
    }

    NovelInfo info = new NovelInfo();

    BeanUtils.copyProperties(novel,info);

    info.setUser(userService.getVOById(novel.getUserId()));
    info.setTags(tagService.getTagsByNovelId(novel.getId()));

    boolean flag =false;
    UserVO userVO = UserHolder.getUser();
    if(userVO != null)
    {
      // 已登录
      if(0 != collectService.query().eq("novelId", id)
          .eq("userId", userVO.getId()).eq("status",0)
          .count() ) {
        // 有收藏记录
        flag = true;
      }
    }
    info.setIsCollected(flag);

    return ResultUtils.success(info);
  }

  /**
   * @return NovelInfo ignore (more,other infos)
   * */
  @Override
  public NovelVO getSimpleInfo(Long id) {
    Novel novel = query().eq("id",id)
        .select("id","userId","name")
        .one();
    if(novel == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR,"not exist this id novel");
    }
    NovelVO info = new NovelVO();

    BeanUtils.copyProperties(novel,info);

    info.setUser(userService.getVOById(novel.getUserId()));

    return info;
  }

  @Override
  public void updateCollectNumByAdd(Long novelId, Long curNum) {
    // get Collect Num
    Novel novel = lambdaQuery().select(Novel::getCollectNum)
        .eq(Novel::getId,novelId).one();
    ThrowUtils.throwIf(novel == null,ErrorCode.OPERATION_ERROR);
    Long collectNum = novel.getCollectNum();

    // update collectNum  = collectNum + curNum
    boolean ret = lambdaUpdate().eq(Novel::getId,novelId)
        .set(Novel::getCollectNum,collectNum+curNum)
        .update();
    ThrowUtils.throwIf(!ret,ErrorCode.OPERATION_ERROR);
  }

  /**
   * @return ignore NovelVO.tags , .User user , description
   * */
  @Override
  public BaseResponse<List<NovelVO>> listByUserId(Long userId) {
    List<Novel> list = lambdaQuery()
        .select(Novel::getId, Novel::getName,Novel::getCover)
        .eq(Novel::getUserId,userId).list();
    if(list.isEmpty()) {
      return ResultUtils.success(null);
    }
    List<NovelVO> novels = new ArrayList<>();
    for( Novel n : list) {
      NovelVO vo = new NovelVO();
      BeanUtils.copyProperties(n,vo);
      // ignore NovelVO.tags , .User user , description
      novels.add(vo);
    }

    return ResultUtils.success(novels);

  }

  @Override
  @Transactional
  public BaseResponse<Boolean> add(NovelAddRequest novelAddRequest) {
    // 校验已登录
    UserVO userVO = UserUtils.LoginCheck();
    if( userVO== null) {
      throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR,"用户未登录");
    }

    Novel one = new Novel();
    one.setCover(UserConstant.NOVEL_DEFAULT_COVER);
    one.setUserId(userVO.getId());
    one.setName(novelAddRequest.getName());
    one.setDescription(novelAddRequest.getDescription());
    boolean ret = save(one);
    ThrowUtils.throwIf(!ret,ErrorCode.OPERATION_ERROR);

    Long novelId = one.getId();
    String tags = novelAddRequest.getTags();
    Collection<Tagmap> tagmaps = new ArrayList<>();
    for(String tagId : tags.split(",")) {
      Tagmap t = new Tagmap();
      t.setNovelId(novelId);
      t.setTagId(Long.parseLong(tagId));
      tagmaps.add(t);
    }
    ret = tagmapService.saveBatch(tagmaps);
    // ??
    tagmaps.clear();
    ThrowUtils.throwIf(!ret,ErrorCode.OPERATION_ERROR);

    return ResultUtils.success(true);

  }

  @Override
  @Transactional
  public BaseResponse<Boolean> updateNovel(NovelUpdateRequest novelUpdateRequest) {
    // 校验已登录
    UserVO userVO = UserUtils.LoginCheck();
    if( userVO== null) {
      throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR,"用户未登录");
    }

    Long id = novelUpdateRequest.getId();
    // 校验权限
    if(!permissionService.checkAdminRight(userVO)
        && !permissionService.checkNovelOwnerRight(userVO.getId(),id)
    ) {
      throw  new BusinessException(ErrorCode.NO_AUTH_ERROR);
    }
    // 校验cover是否存在
    String cover = novelUpdateRequest.getCover();
    if(StringUtils.isNotBlank(cover) && !FileUtils.isImgFileExist(cover)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR,"图片路径文件不存在");
    }
    String name = novelUpdateRequest.getName();
    String description = novelUpdateRequest.getDescription();
    Integer isEnd = novelUpdateRequest.getIsEnd();

    // complex to update tags (较复杂，暂时忽略): table tagmap, temp ignore
  //    String tags = novelUpdateRequest.getTags();

    boolean ret = lambdaUpdate().eq(Novel::getId,id)
        .set(StringUtils.isNotBlank(cover),Novel::getCover,cover)
        .set(StringUtils.isNotBlank(name),Novel::getName,name)
        .set(StringUtils.isNotBlank(description),Novel::getDescription,description)
        .set(isEnd !=null && (isEnd ==1 || isEnd == 0),Novel::getIsEnd,isEnd)
        .update();
    ThrowUtils.throwIf(!ret,ErrorCode.OPERATION_ERROR,"update novel error");
    return ResultUtils.success(true);
  }

  @Override
  public BaseResponse<Boolean> delNovel(Long id) {
    // 校验已登录
    UserVO userVO = UserUtils.LoginCheck();
    if( userVO== null) {
      throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR,"用户未登录");
    }
    // 校验权限
    if(!permissionService.checkAdminRight(userVO)
        && !permissionService.checkNovelOwnerRight(userVO.getId(),id)
    ) {
      throw  new BusinessException(ErrorCode.NO_AUTH_ERROR);
    }

    int result = getBaseMapper().deleteById(id);
    ThrowUtils.throwIf(result<=0 , ErrorCode.OPERATION_ERROR,"删除小说失败");

    return ResultUtils.success(true);
  }

  @Override
  public List<NovelVO> listMaxCollects(int limit) {
    return getBaseMapper().listMaxCollects(limit);
  }


// private functions:


}




